﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Moop.Common;
using Moop.DataLayer.Olap;
using Microsoft.Practices.Unity;
using Microsoft.AnalysisServices.AdomdClient;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using Moop.Common.IFaces;

namespace Moop.DataLayer.Olap.Service
{
    public class OlapDataService : IOlapDataService
    {
        #region IOlapDataService Members

        public string DastabaseName { get; set; }

        [Dependency]
        public IOlapMetaDataService OlapMetaDataService { get; set; }

        #endregion

        public OlapDataService()
        {
        }


        public void Init()
        {
            #region Init MetaData Service

            OlapMetaDataService.Init();
            OlapMetaDataService.DatabaseName = DastabaseName;
            OlapMetaDataService.Populate();

            #endregion
        }


        public IAsyncResult BeginPopulate(object input, AsyncCallback ac, object state)
        {
            Task<IOlapDatabase> f = Task<IOlapDatabase>.Factory.StartNew((s) => Populate(), state, TaskCreationOptions.None);
            if (ac != null)
            {
                f.ContinueWith((res) => ac(f))
                 .ContinueWith((Task previous) => { var e = previous.Exception; throw e; }, TaskContinuationOptions.OnlyOnFaulted);
            }
            return f;
        }


        public IOlapDatabase EndPopulate(IAsyncResult ar)
        {
            if (((Task<IOlapDatabase>)ar).IsFaulted)
            {
                return null;
            }
            return ((Task<IOlapDatabase>)ar).Result;
        }


        public IOlapDatabase Populate()
        {
            Init();
            //ShallowPopulate();
            return OlapMetaDataService.OlapDatabase;
        }


        private void ShallowPopulate()
        {
            using (AdomdConnection conn = OlapMetaDataService.OlapClient.GetConnection())
            {
                foreach (ICube cube in OlapMetaDataService.OlapDatabase.Cubes.Values)
                {
                    foreach (ICubeDimension dim in cube.CubeDimensions.Values)
                    {
                        #region Sets

                        //Sets - Not Applicaple as it doesnt have Values is an embdeded
                        //MDX Expression Stored in Cube: http://technet.microsoft.com/en-us/library/ms166594.aspx

                        #endregion


                        #region Attributes

                        Parallel.ForEach(dim.Attributes, attr => GetAttributes(cube, dim, attr));

                        #endregion


                        #region Hierrarchies

                        Parallel.ForEach(dim.Hierarchies, hier => GetHierarchies(cube, dim, hier));

                        #endregion

                    }
                }
            }
        }


        private void GetAttributes(ICube cube, ICubeDimension dim, ICubeDimensionAttribute attr)
        {
            using (AdomdConnection conn = OlapMetaDataService.OlapClient.GetConnection())
            {
                TupleCollection tuples; string qry;
                qry = GetAttributesQuery(cube, dim, attr);
                tuples = GetCollection(conn, qry);

                if (tuples.Count > 0)
                {
                    foreach (Microsoft.AnalysisServices.AdomdClient.Tuple tuple in tuples)
                    {
                        if (tuple.Members.Count > 0)
                        {
                            attr.Values.Add(tuple.Members[0].Name, System.Tuple.Create(tuple.Members[0].Name, new object(), tuple.Members[0].Caption));
                        }
                    }
                }
            }
        }


        private void GetHierarchies(ICube cube, ICubeDimension dim, ICubeDimensionHierarchy hier)
        {
            using (AdomdConnection conn = OlapMetaDataService.OlapClient.GetConnection())
            {
                foreach (ICubeDimensionHierarchy dimHierarchy in dim.Hierarchies)
                {
                    foreach (ICubeDimensionHierarchyLevel level in dimHierarchy.Levels)
                    {
                        TupleCollection tuples = null; string qry;
                        qry = GetHierarchyLevelQuery(cube, dim, dimHierarchy, level);
                        tuples = GetCollection(conn, qry);

                        if (tuples.Count > 0)
                        {
                            foreach (Microsoft.AnalysisServices.AdomdClient.Tuple tuple in tuples)
                            {
                                if (tuple.Members.Count > 0)
                                {
                                    level.Name = tuple.Members[0].Name;
                                    level.Caption = tuple.Members[0].Caption;
                                }
                            }
                        }
                    }
                }
            }
        }


        #region Olap Helper Methods

        private string GetAttributesQuery(ICube cube, ICubeDimension dim, ICubeDimensionAttribute dimAttr)
        {
            return "SELECT SUBSET(" + dimAttr.OlapPath + ".MEMBERS, 0, 5) ON 0 FROM [" + cube.Name + "]";
        }


        private string GetHierarchyQuery(ICube cube, ICubeDimension dim, ICubeDimensionHierarchy dimHierarchy)
        {
            return "SELECT SUBSET([" + dim.Name + "]." + dimHierarchy.OlapPath + ".MEMBERS, 0, 5) ON 0 FROM [" + cube.Name + "]";
        }


        private string GetHierarchyLevelQuery(ICube cube, ICubeDimension dim, ICubeDimensionHierarchy dimHierarchy, ICubeDimensionHierarchyLevel dimHierarchyLevel)
        {
            return "SELECT {" + dimHierarchyLevel.OlapPath + ".MEMBERS} ON 0 FROM [" + cube.Name + "]";
        }


        private TupleCollection GetCollection(AdomdConnection conn, string qry)
        {
            AdomdCommand cmd = new AdomdCommand(qry, conn);
            CellSet result = cmd.ExecuteCellSet();
            TupleCollection tuples = result.Axes[0].Set.Tuples;
            return tuples;
        }

        #endregion
    }
}
